/* Includes ------------------------------------------------------------------*/

/* Private includes ----------------------------------------------------------*/
#include "main.h"
#include "tx_api.h"
#include "jpeg_utils.h"
#include "shell.h"

#include "DWT.h"

/* Private define ------------------------------------------------------------*/
#define TASK_STACK_SIZE 10240
#define Height 480
#define Width 640

#define Jpeg_Buffer_Size 0x20000

#define FRAME_READY_EVENT0 (1 << 0)
#define FRAME_READY_EVENT1 (1 << 1)

/* Private typedef -----------------------------------------------------------*/
typedef struct
{
    uint32_t len;
    uint8_t data[Jpeg_Buffer_Size - sizeof(uint32_t)];
} jpeg_t;

/* Private macro -------------------------------------------------------------*/

/* Global variables ---------------------------------------------------------*/

/* External function ---------------------------------------------------------*/
extern void jpeg_init();
extern uint32_t jpeg_encode(uint8_t *p_image_data, uint8_t *p_image_data2, uint8_t *p_jpeg_buffer);

extern HAL_StatusTypeDef My_HAL_DCMI_Start_DMA(DCMI_HandleTypeDef *hdcmi, uint32_t DCMI_Mode, uint32_t pData, uint32_t SecondMemAddress, uint32_t Length);

extern int usb_write(uint8_t *data, uint32_t len);
extern int usb_read(uint8_t *data, uint32_t len);

/* External variables ---------------------------------------------------------*/
extern DCMI_HandleTypeDef hdcmi;
extern JPEG_HandleTypeDef hjpeg;

/* Private variables ---------------------------------------------------------*/
static TX_THREAD tx_task;
static uint8_t task_stack[TASK_STACK_SIZE];

static TX_SEMAPHORE tx_dcmi_semaphore;
static TX_EVENT_FLAGS_GROUP tx_event_flags_group;

static uint8_t frame_buffer_0A[Height * Width / 2] __attribute__((section(".DcmiPool_0A")));
static uint8_t frame_buffer_0B[Height * Width / 2] __attribute__((section(".DcmiPool_0B")));
static uint8_t frame_buffer_1A[Height * Width / 2] __attribute__((section(".DcmiPool_1A")));
static uint8_t frame_buffer_1B[Height * Width / 2] __attribute__((section(".DcmiPool_1B")));

static uint8_t jpeg_buffer[Jpeg_Buffer_Size] __attribute__((section(".JpegPoolSection")));

static int is_streaming = 0;

volatile static uint32_t t0, t1, t2;

/* Private Function definitions -----------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/
static void task_entry(ULONG arg)
{
    int frame_sample_index = 0;
    int frame_ready_index = 0;

    // memset(&frame_buffer_0A, 0xCC, sizeof(frame_buffer_0A));
    // memset(&frame_buffer_0B, 0xCC, sizeof(frame_buffer_0B));
    // memset(&frame_buffer_1A, 0xCC, sizeof(frame_buffer_1A));
    // memset(&frame_buffer_1B, 0xCC, sizeof(frame_buffer_1B));

    jpeg_init();

    // HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)&frame_buffer_0A, Width * Height / 4);
    My_HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)&frame_buffer_0A, (uint32_t)&frame_buffer_0B, Width * Height / 4);
    while (1)
    {
        tx_semaphore_get(&tx_dcmi_semaphore, TX_WAIT_FOREVER);

        frame_ready_index = frame_sample_index;
        frame_sample_index = frame_sample_index ^ 1;
        if (frame_sample_index == 0)
            My_HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)&frame_buffer_0A, (uint32_t)&frame_buffer_0B, Width * Height / 4);
        else
            My_HAL_DCMI_Start_DMA(&hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)&frame_buffer_1A, (uint32_t)&frame_buffer_1B, Width * Height / 4);
        __HAL_DCMI_ENABLE_IT(&hdcmi, DCMI_IT_FRAME);

        t1 = DWT_CNT;
        DWT_INIT;
        t0 = DWT_CNT;

        if (is_streaming == 0)
            continue;

        tx_event_flags_set(&tx_event_flags_group, ~(FRAME_READY_EVENT0 | FRAME_READY_EVENT1), TX_AND);
        if (frame_ready_index == 0)
        {
            tx_event_flags_set(&tx_event_flags_group, FRAME_READY_EVENT0, TX_OR);
        }
        else
        {
            tx_event_flags_set(&tx_event_flags_group, FRAME_READY_EVENT1, TX_OR);
        }
    }
}

static int start_streaming()
{
    is_streaming = 1;
    tx_event_flags_set(&tx_event_flags_group, ~(FRAME_READY_EVENT0 | FRAME_READY_EVENT1), TX_AND);
    return 0;
}

static int stop_streaming()
{
    is_streaming = 0;
    return 0;
}

/**********************
 * @brief jpeg压缩与usb发送串行执行, 否则容易出错jpeg编码异常, 编码一半结束
 * 修改 ux_device_class_cdc_acm_write _ux_utility_memory_copy, 修改直接赋值, 可提高USB性能
 */
static int get_frame()
{
    UINT status;
    uint32_t length = 0;
    jpeg_t *p_jpeg_t = (jpeg_t *)&jpeg_buffer;

    ULONG actual_flags;
    is_streaming = 1;
    status = tx_event_flags_get(&tx_event_flags_group, FRAME_READY_EVENT0 | FRAME_READY_EVENT1, TX_OR_CLEAR, &actual_flags, 1000);
    if (status != TX_SUCCESS)
    {
        usb_write((uint8_t *)&length, sizeof(uint32_t));
        return -1;
    }

    DWT_INIT;
    t0 = DWT_CNT;

    if ((actual_flags & FRAME_READY_EVENT0) == FRAME_READY_EVENT0)
    {
        p_jpeg_t->len = jpeg_encode((uint8_t *)&frame_buffer_0A, (uint8_t *)&frame_buffer_0B, p_jpeg_t->data);
    }
    else
    {
        p_jpeg_t->len = jpeg_encode((uint8_t *)&frame_buffer_1A, (uint8_t *)&frame_buffer_1B, p_jpeg_t->data);
    }
    t1 = DWT_CNT;

    /* 检查jpeg编码是否正常, 调试用. */
    int i;
    for (i = 0; i < 32; i++)
    {
        if (p_jpeg_t->data[p_jpeg_t->len - i - 1] != 0x00)
            break;
    }
    if (p_jpeg_t->data[p_jpeg_t->len - i - 1] != 0xD9 || p_jpeg_t->data[p_jpeg_t->len - i - 2] != 0xFF)
    {
        __asm__("NOP");
    }

    usb_write((uint8_t *)p_jpeg_t, p_jpeg_t->len + sizeof(uint32_t));
    return 0;
}

/* Public Function definitions -----------------------------------------------*/
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *p_hdcmi)
{
    HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_11); // PD11

    HAL_DCMI_Stop(&hdcmi);
    __HAL_DCMI_DISABLE(&hdcmi);
    tx_semaphore_put(&tx_dcmi_semaphore);
}

void camera_init()
{
    tx_semaphore_create(&tx_dcmi_semaphore, "dcmi semaphore", 0);
    tx_event_flags_create(&tx_event_flags_group, "dcmi events");

    /* 执行jpeg编码任务的优先级不能高于 jpeg encode_dma中任务的优先级, 否则会导致Jpeg断裂 */
    tx_thread_create(&tx_task, "camera task", task_entry, 0,
                     task_stack, TASK_STACK_SIZE,
                     1u, 1u, TX_NO_TIME_SLICE, TX_AUTO_START);
}

/* Testbench ------------------------------------------------------------*/

/****************** letter shell register *********************************/
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), start_streaming, start_streaming, );
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), stop_streaming, stop_streaming, );
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), get_frame, get_frame, return length);